package com.lanyou.esb.service;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.rmi.NoSuchObjectException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.springframework.stereotype.Service;

import com.google.gson.JsonObject;
import com.lanyou.esb.common.CommonUtils;
import com.lanyou.esb.datadictionary.ContentType;
import com.lanyou.esb.datadictionary.IfReqType;
import com.lanyou.esb.datadictionary.IfType;
import com.lanyou.esb.web.param.ServiceTestingParam;

/**
 * 服务测试
 * 
 * @author Davey.wu
 */
@Service
public class ServiceTestingService {

	@PersistenceContext
	private EntityManager entityManager;

	/**
	 * 服务测试请求发送
	 * 
	 * @param param
	 *            :请求参数
	 * @param responseContent
	 *            ：是否需要解析响应体
	 * @return
	 * @throws HttpException
	 * @throws IOException
	 * @throws URISyntaxException
	 */
	public static Map<String, Object> sendRequest(ServiceTestingParam param,
			boolean responseContent) throws HttpException, IOException,
			URISyntaxException {
		Map<String, Object> map = new HashMap<>();
		String parameter = param.getParameters();
		IfReqType method = IfReqType.get(param.getRequest() != null ? param
				.getRequest() : "0");
		// 如果是web service接口，默认使用POST请求
		if (IfType.WS.ValueCode.equals(param.getInterfaceType())
				&& IfReqType.POST != method) {
			method = IfReqType.POST;
		}
		String responseMsg = "";
		// 获取HTTP内容类型
		String contentType = getContentType(param.getContentType());
		Map<String, String> headerMap = new HashMap<>();
		String param4rest = "";
		// 如果不是SOAP请求
		if (!IfType.WS.ValueCode.equals(param.getInterfaceType())) {
			if (StringUtils.isNotBlank(parameter)) {
				try {
					String headers = parameter.substring(1,
							parameter.length() - 1).replaceAll("\"", "");
					String[] strA = headers.split(",");
					if (strA != null && strA.length > 0) {
						for (String str : strA) {
							String[] strV = str.split(":");
							if (strV != null && strV.length == 2) {
								// request.addHeader(strV[0], strV[1]);
								headerMap.put(strV[0], strV[1]);
								param4rest += strV[0] + "=" + strV[1] + "&";
							}
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		HttpRequest request;
		if (StringUtils.isNotBlank(parameter)) {
			request = new BasicHttpEntityEnclosingRequest(method.name(),
					param.getUrl()
							+ (StringUtils.isBlank(param4rest) ? ""
									: ("?" + param4rest)));
			// 添加输入流
			byte[] b = parameter.getBytes(CommonUtils.ENCODING_TYPE_UTF8);
			InputStream is = new ByteArrayInputStream(b, 0, b.length);
			((HttpEntityEnclosingRequest) request)
					.setEntity(new InputStreamEntity(is, b.length));
		} else {
			request = new BasicHttpRequest(method.name(), param.getUrl()
					+ (StringUtils.isBlank(param4rest) ? ""
							: ("?" + param4rest)));
		}
		// 如果不是SOAP请求，将验证信息加入请求头
		if (!IfType.WS.ValueCode.equals(param.getInterfaceType())) {
			String json = createAuthInfo(param);
			request.addHeader(CommonUtils.PROXY_HEADER, json);
			if (headerMap.size() > 0) {
				for (Entry<String, String> entry : headerMap.entrySet()) {
					request.addHeader(entry.getKey(), entry.getValue());
				}
			}
		}
		request.addHeader(CommonUtils.CONTENT_TYPE, contentType);
		if (StringUtils.isNotBlank(param.getSoapActionUri())) {
			request.addHeader(CommonUtils.SOAP_ACTION_URI,
					param.getSoapActionUri());
		}
		HttpConnectionParams.setConnectionTimeout(request.getParams(), 2000);
		int statusCode = -1;
		try {
			// 最后生成一个HttpClient对象，并发出postMethod请求
			HttpClient httpClient = new DefaultHttpClient();
			HttpHost httpHost = new HttpHost(URIUtils.extractHost(new URI(param
					.getUrl())));
			HttpResponse response = httpClient.execute(httpHost, request);
			statusCode = response.getStatusLine().getStatusCode();
			if (responseContent && 200 == statusCode) {
				try {
					responseMsg = EntityUtils.toString(response.getEntity(),
							CommonUtils.ENCODING_TYPE_UTF8);
					if (responseMsg != null) {
						if (responseMsg.length() > CommonUtils.SERVICE_TEST_RES_MSG_DEFAULT_LENGTH) {
							String temp = responseMsg
									.substring(
											0,
											CommonUtils.SERVICE_TEST_RES_MSG_DEFAULT_LENGTH)
									+ CommonUtils.MSG_SUFFER;
							responseMsg = temp;
						}
					}
				} catch (Exception e) {
					responseMsg = "调用失败，详细信息 ：【" + e.getClass().getName() + ":"
							+ e.getMessage() + "】";
				}
			} else if (responseContent && 200 != statusCode) {
				responseMsg = response.getStatusLine().getReasonPhrase();
				responseMsg = "调用失败！错误码：" + statusCode
						+ "。详细信息："// +new
									// String(responseMsg.getBytes("iso-8859-1"),"utf-8");
						+ URLDecoder.decode(responseMsg,
								CommonUtils.ENCODING_TYPE_UTF8);
			}

		} catch (Exception e) {
			if (e instanceof UnknownHostException) {
				responseMsg = "未知的主机：" + e.getClass().getName() + ":"
						+ e.getMessage();
			} else if (e instanceof ConnectTimeoutException) {
				responseMsg = "连接超时：" + e.getClass().getName() + ":"
						+ e.getMessage();
			} else {
				responseMsg = "服务异常：" + e.getClass().getName() + ":"
						+ e.getMessage();
			}

		}
		map.put(CommonUtils.MSG_FIELD_RESULT, responseMsg);
		map.put(CommonUtils.RESPONDSE_STATUS_CODE, statusCode);
		return map;
	}

	/**
	 * 将对象转换成XML
	 * 
	 * @param map
	 * @return
	 */
	public static String toXml(Map<String, Object> map) {
		Document document = DocumentHelper.createDocument();
		Element nodeElement = document.addElement("node");
		for (Entry<String, Object> entry : map.entrySet()) {
			Element keyElement = nodeElement.addElement(entry.getKey());
			keyElement.setText(String.valueOf(entry.getValue()));
		}
		// 使用输出流来进行转化
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		// 使用UTF-8编码
		OutputFormat format = new OutputFormat("   ", true,
				CommonUtils.ENCODING_TYPE_UTF8);
		XMLWriter writer = null;
		String result = "";
		try {
			writer = new XMLWriter(out, format);
			writer.write(document);
			result = out.toString(CommonUtils.ENCODING_TYPE_UTF8);
		} catch (Exception e) {
			e.printStackTrace();
			result = "";
		} finally {
			if (writer != null) {
				try {
					writer.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

		return result;
	}

	/**
	 * 格式化SOAP请求报文，正则匹配SOAP参数，并为参数赋值
	 * 
	 * 匹配的类型有以下两种： <br>
	 * 1，<(允许有0个或多个空格)parameter_name(允许有0个或多个空格)>value</(允许有0个或多个空格
	 * )parameter_name(允许有0个或多个空格)> <br>
	 * 2，<(空或者(非：后加一个：))(允许有0个或多个空格)parameter_name
	 * (允许有0个或多个空格)>value</(空或者(非：后加一个：))(允许有0个或多个空格
	 * )parameter_name(允许有0个或多个空格)>
	 * 
	 * @param soap
	 * @param map
	 * @return
	 */
	public static String formatSoapMsg(String soap, Map<String, Object> map) {
		if (StringUtils.isBlank(soap)) {
			return "";
		}
		if (map == null || map.size() == 0) {
			return soap;
		}
		for (Entry<String, Object> entry : map.entrySet()) {
			String key = entry.getKey();
			String value = (String) entry.getValue();
			Pattern pattern = Pattern.compile("<(((?!:).)*:)?\\s*" + key
					+ "\\s*>.*</(((?!:).)*:)?\\s*" + key + "\\s*>");
			Matcher matches = pattern.matcher(soap);
			if (matches.find()) {
				String subStr = matches.group(0);
				subStr = Pattern.compile(">.*<").matcher(subStr)
						.replaceFirst(">" + value + "<");
				soap = matches.replaceFirst(subStr);
			}
		}
		return soap;
	}

	/**
	 * 格式化请求信息：添加校验信息头
	 * 
	 * @param authType
	 * @param requestMess
	 * @return
	 */
	public String formateRequestMess(String authType, Map<String, Object> map,
			String requestMess) {
		if (StringUtils.isBlank(requestMess)) {
			return requestMess;
		}
		// if (!AuthencationModeType.HTTP_BASIC_KEY.ValueCode.equals(authType)
		// && !AuthencationModeType.WS_SECURITY.ValueCode.equals(authType)) {
		// return requestMess;
		// }
		// requestMess = requestMess.replaceAll("\t", "");
		// requestMess = requestMess.replaceAll("\n", "");
		String SOAP_ENV = "soap";
		if (requestMess.contains(CommonUtils.SOAPENV + ":")) {
			SOAP_ENV = CommonUtils.SOAPENV;
		}

		// 第一种情况，有头部标签，且是<header>..</header>这种形式
		Pattern pattern = Pattern.compile("<\\s*" + SOAP_ENV + ":\\s*"
				+ CommonUtils.HEADER + "\\s*>");
		Pattern pattern2 = Pattern.compile("</\\s*" + SOAP_ENV + ":\\s*"
				+ CommonUtils.HEADER + "\\s*>");
		Matcher matches = pattern.matcher(requestMess);
		Matcher matches2 = pattern2.matcher(requestMess);
		if (matches.find() && matches2.find()) {
			String subStr = matches.group(0);
			String subStr2 = matches2.group(0);
			int index1 = requestMess.indexOf(subStr);
			int index2 = requestMess.indexOf(subStr2);
			String content = requestMess.substring(index1 + subStr.length(),
					index2);
			// 有内容，且包含认证请求头标识符
			if (StringUtils.isNotBlank(content)
					&& content.contains(CommonUtils.PROXY_HEADER)) {
				// 包含认证所需的所有信息
				if (contains(content,
						map.keySet().toArray(new String[map.size()]))) {
					String newStr = formatSoapMsg(content, map);
					return requestMess.replaceFirst(content, newStr);
				}
				// TODO 至少有部分认证信息缺失
			}
			// 不包含包含认证请求头标识符
			Pattern patternEvn = Pattern.compile(CommonUtils.XMLNS
					+ ":((?!:).)*=");
			Matcher matchesEvn = patternEvn.matcher(requestMess);
			String namespace = "";
			while (matchesEvn.find()) {
				String m = matchesEvn.group(0);
				if (!m.contains(SOAP_ENV)) {
					namespace = m.substring(m.indexOf(":") + 1, m.length() - 1);
					break;
				}

			}
			// String target = "<" + target + ":" + key + ">" + applyKey + "</"
			// + target
			// + ":" + key + ">";
			String target = generateAuthInfo(namespace, map);
			target = subStr + target + content + subStr2;
			return requestMess.replaceFirst(subStr + content + subStr2, target);

		}
		// 第二种情况，有头部标签,但是<header/>这种形式
		pattern = Pattern.compile("<\\s*" + SOAP_ENV + ":\\s*"
				+ CommonUtils.HEADER + "\\s*/>");
		matches = pattern.matcher(requestMess);
		if (matches.find()) {
			String subStr = matches.group(0);
			Pattern patternEvn = Pattern.compile(CommonUtils.XMLNS
					+ ":((?!:).)*=");
			Matcher matchesEvn = patternEvn.matcher(requestMess);
			String namespace = "";
			while (matchesEvn.find()) {
				String m = matchesEvn.group(0);
				if (!m.contains(SOAP_ENV)) {
					namespace = m.substring(m.indexOf(":") + 1, m.length() - 1);
					break;
				}

			}
			// target = "<" + target + ":" + key + ">" + applyKey + "</" +
			// target
			// + ":" + key + ">";
			String target = generateAuthInfo(namespace, map);
			target = subStr.substring(0, subStr.length() - 2) + ">" + target
					+ "</" + subStr.substring(1, subStr.length() - 2) + ">";
			return requestMess.replaceFirst(subStr, target);
		}

		// 第三种情况，没有头部标签
		pattern = Pattern.compile("<\\s*" + SOAP_ENV + ":\\s*"
				+ CommonUtils.BODY + "\\s*>");
		matches = pattern.matcher(requestMess);
		if (matches.find()) {
			String subStr = matches.group(0);
			Pattern patternEvn = Pattern.compile(CommonUtils.XMLNS
					+ ":((?!:).)*=");
			Matcher matchesEvn = patternEvn.matcher(requestMess);
			String namespace = "";
			while (matchesEvn.find()) {
				String m = matchesEvn.group(0);
				if (!m.contains(SOAP_ENV)) {
					namespace = m.substring(m.indexOf(":") + 1, m.length() - 1);
					break;
				}
			}
			int index = subStr.indexOf(":");
			// target = "<" + target + ":" + key + ">" + applyKey + "</" +
			// target
			// + ":" + key + ">";
			String target = generateAuthInfo(namespace, map);
			target = subStr.substring(0, index) + ":" + CommonUtils.HEADER
					+ ">" + target + "</" + subStr.substring(1, index) + ":"
					+ CommonUtils.HEADER + ">" + subStr;
			return requestMess.replaceFirst(subStr, target);
		}
		return requestMess;
	}

	/**
	 * 生成认证信息头
	 * 
	 * @param namespace
	 * @param map
	 * @return
	 */
	private String generateAuthInfo(String namespace, Map<String, Object> map) {
		StringBuilder builder = new StringBuilder();
		String suffer = StringUtils.isBlank(namespace) ? "" : namespace + ":";
		builder.append("<" + suffer + CommonUtils.PROXY_HEADER + ">");
		if (map != null && !map.isEmpty()) {
			for (Entry<String, Object> entry : map.entrySet()) {
				builder.append("<" + entry.getKey() + ">" + entry.getValue()
						+ "</" + entry.getKey() + ">");
			}
		}
		builder.append("</" + suffer + CommonUtils.PROXY_HEADER + ">");
		return builder.toString();
	}

	/**
	 * 判断是否包含字符串
	 * 
	 * @param content
	 * @param array
	 * @return
	 */
	private boolean contains(String content, String[] keys) {
		if (StringUtils.isBlank(content) || keys == null || keys.length == 0) {
			return false;
		}
		for (String key : keys) {
			if (!content.contains(key)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 生成请求头验证信息
	 * 
	 * @param param
	 * @return
	 */
	private static String createAuthInfo(ServiceTestingParam param) {
		JsonObject jsonObject = new JsonObject();
		jsonObject.addProperty(CommonUtils.APPLY_KEY, param.getApplyKey());
		jsonObject
				.addProperty(CommonUtils.ACCESS_TOKEN, param.getAccessToken());
		jsonObject.addProperty(CommonUtils.CALL_SYSTEM_CODE,
				param.getCallSystemCode());
		return jsonObject.toString();
	}

	/**
	 * 获取HTTP内容类型
	 * 
	 * @param contentType
	 * @return
	 * @throws NoSuchObjectException
	 */
	private static String getContentType(String contentType)
			throws NoSuchObjectException {
		if (StringUtils.isBlank(contentType)) {
			return CommonUtils.CONTENTYPE_APP_JSON;
		}
		if (ContentType.SOAP11.ValueCode.equals(contentType)) {
			return CommonUtils.CONTENTYPE_TEXT_XML;
		}
		if (ContentType.SOAP12.ValueCode.equals(contentType)) {
			return CommonUtils.CONTENTYPE_APP_SOAP_XML;
		}
		return CommonUtils.CONTENTYPE_APP_JSON;
	}
}
